/*
 * Copyright (c) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * 该cpp文件基于OpenCV实现了网球检测功能。为了保证FPS的帧数，
 * 我们设计的原则是IVE（Intelligent Video Engine）+AI CPU结合使用，即IVE不支持的算子
 * 通过AI CPU进行计算，否则走IVE硬件加速模块进行处理。并将检测的结果通过VGS标记出来。
 *
 * The cpp file implements the tennis ball detection function based on OpenCV.
 * In order to ensure the number of FPS frames,
 * The principle of our design is the combination of IVE (Intelligent Video Engine) + AI CPU,
 * that is, operators not supported by IVE are calculated by AI CPU, otherwise,
 * the IVE hardware acceleration module is used for processing.
 * And the detection results are marked by VGS.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sample_comm_ive.h"
#include "ive_img.h"
#include "vgs_img.h"

#include "trace.h"
#include "sample_ive_queue.h"
#include "svp_nnie_application.h"

#include "lbph_tracking.h"

#define TRACK_MAX_NUM 5
#define IVE_HIST_NUM 256
#define UNIFORM_PATTERN_COUNT 59

static SAMPLE_IVE_LBPH_S s_stLBPH[TRACK_MAX_NUM];
static SAMPLE_IVE_FEATURE_MATCH_S s_stMatchParams;

static HI_U8 uniform_patterns[IVE_HIST_NUM] = { 0 };    // 均匀模式LBP映射表
static RectBox faceRectBboxs[TRACK_MAX_NUM];

static HI_U8 matchTargetNums = 0;
static HI_U8 faceTargetNums = 0;
static HI_U8 lastTargetNums = 0;

static HI_BOOL s_bStopSignal = HI_FALSE;

static IVE_IMAGE_S srcImg;

HI_VOID SAMPLE_IVE_LBPH_HandleSig( void )
{
    s_bStopSignal = HI_TRUE;
}

static HI_VOID SAMPLE_IVE_LBPH_Uninit(SAMPLE_IVE_LBPH_S *pstLBPH, SAMPLE_IVE_FEATURE_MATCH_S *pstMatchParams)
{
    HI_U8 i;
    for (i = 0; i < TRACK_MAX_NUM; i++)
    {
        IVE_MMZ_FREE(pstLBPH[i].stGrayImg.au64PhyAddr[0], pstLBPH[i].stGrayImg.au64VirAddr[0]);
        IVE_MMZ_FREE(pstLBPH[i].stEqualHistImg.au64PhyAddr[0], pstLBPH[i].stEqualHistImg.au64VirAddr[0]);
        IVE_MMZ_FREE(pstLBPH[i].stEaualHistCtrl.stMem.u64PhyAddr, pstLBPH[i].stEaualHistCtrl.stMem.u64VirAddr);
        IVE_MMZ_FREE(pstLBPH[i].stLbpImg.au64PhyAddr[0], pstLBPH[i].stLbpImg.au64VirAddr[0]);
    }

    IVE_MMZ_FREE(pstMatchParams->stMatchMem.u64PhyAddr, pstMatchParams->stMatchMem.u64VirAddr);
    IVE_MMZ_FREE(pstMatchParams->stFeatureMem.u64PhyAddr, pstMatchParams->stFeatureMem.u64VirAddr);
    IVE_MMZ_FREE(srcImg.au64PhyAddr[0], srcImg.au64VirAddr[0]);
    pstMatchParams->isTaskReady = HI_FALSE;
    pstMatchParams->isNewMatch = HI_TRUE;
    pstMatchParams->isUpdate = HI_FALSE;
    pstMatchParams->isProcess = HI_FALSE;
    pstMatchParams->score = 0;
}

static HI_S32 SAMPLE_IVE_LBPH_Init(SAMPLE_IVE_LBPH_S *pstLBPH, SAMPLE_IVE_FEATURE_MATCH_S *pstMatchParams, HI_U32 u32Width, HI_U32 u32Height)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_U32 u32Size;
    HI_U16 i;

    for (i = 0; i < TRACK_MAX_NUM; i++)
    {
        (HI_VOID)memset_s(&(pstLBPH[i]), sizeof(SAMPLE_IVE_LBPH_S), 0, sizeof(SAMPLE_IVE_LBPH_S));

        s32Ret = SAMPLE_COMM_IVE_CreateImage(&(pstLBPH[i].stEqualHistImg), IVE_IMAGE_TYPE_U8C1, u32Width, u32Height);
        SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, LBPH_INIT_FAIL, "Error(%#x),Create EqualHist image failed!\n", s32Ret);
        s32Ret = SAMPLE_COMM_IVE_CreateImage(&(pstLBPH[i].stLbpImg), IVE_IMAGE_TYPE_U8C1, u32Width, u32Height);
        SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, LBPH_INIT_FAIL, "Error(%#x),Create Lbp image failed!\n", s32Ret);

        u32Size = sizeof(IVE_EQUALIZE_HIST_CTRL_MEM_S);
        s32Ret = SAMPLE_COMM_IVE_CreateMemInfo(&(pstLBPH[i].stEaualHistCtrl.stMem), u32Size);
        SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, LBPH_INIT_FAIL, "Error(%#x),Create stEaualHistCtrl mem info failed!\n", s32Ret);
        (HI_VOID)memset_s(SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(HI_VOID, pstLBPH[i].stEaualHistCtrl.stMem.u64VirAddr),
            pstLBPH[i].stEaualHistCtrl.stMem.u32Size, 0, pstLBPH[i].stEaualHistCtrl.stMem.u32Size);

        pstLBPH[i].stLbpCtrl.enMode = IVE_LBP_CMP_MODE_NORMAL;
        pstLBPH[i].stLbpCtrl.un8BitThr.s8Val = 0;
    }

    pstMatchParams->grid_w = 8;
    pstMatchParams->grid_h = 8;
    uint8_t block_rows = 64 / s_stMatchParams.grid_w;
    uint8_t block_cols = 64 / s_stMatchParams.grid_h;
    u32Size = block_rows * block_cols * UNIFORM_PATTERN_COUNT * sizeof(HI_U8);
    s32Ret = SAMPLE_COMM_IVE_CreateMemInfo(&(pstMatchParams->stMatchMem), u32Size);
    SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, LBPH_INIT_FAIL, "Error(%#x),Create stMatchMem mem info failed!\n", s32Ret);
        (HI_VOID)memset_s(SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(HI_VOID, pstMatchParams->stMatchMem.u64VirAddr),
            pstMatchParams->stMatchMem.u32Size, 0, pstMatchParams->stMatchMem.u32Size);

    s32Ret = SAMPLE_COMM_IVE_CreateMemInfo(&(pstMatchParams->stFeatureMem), u32Size);
    SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, LBPH_INIT_FAIL, "Error(%#x),Create stFeatureMem mem info failed!\n", s32Ret);
        (HI_VOID)memset_s(SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(HI_VOID, pstMatchParams->stFeatureMem.u64VirAddr),
            pstMatchParams->stFeatureMem.u32Size, 0, pstMatchParams->stFeatureMem.u32Size);

    s32Ret = SAMPLE_COMM_IVE_CreateImage(&(srcImg), IVE_IMAGE_TYPE_U8C1, 320, 320);
        SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, LBPH_INIT_FAIL, "Error(%#x),Create copy src image failed!\n", s32Ret);

    pstMatchParams->isTaskReady = HI_FALSE;
    pstMatchParams->isNewMatch = HI_TRUE;
    pstMatchParams->isUpdate = HI_FALSE;
    pstMatchParams->isProcess = HI_FALSE;
    pstMatchParams->score = 0;

    s32Ret = HI_SUCCESS;

LBPH_INIT_FAIL:
    if (s32Ret != HI_SUCCESS) {
        SAMPLE_IVE_LBPH_Uninit(pstLBPH, pstMatchParams);
    }
    return s32Ret;
}

/*
 * 将YUV P/SP视频帧裁剪成U8C1图像（灰度图像）
 * cut YUV P/SP video frame to U8C1 image(Grayscale image)
 */
static int IVE_LBPH_FRAME_TO_GRAY(const IVE_IMAGE_S *src, SAMPLE_IVE_LBPH_S *pstLBPH, 
    const RectBox *origBox, int dstWidth, int dstHeight)
{
    HI_U8 i;
    int ret;

    /* check input frame */
    HI_ASSERT(src->au64VirAddr[0]);
    HI_ASSERT(src->au32Stride[0] >= src->u32Width);

    for (i = 0; i < matchTargetNums; i++)
    {
        IntRectBox box;
        box.xmin = (int)(origBox[i].xmin);
        box.xmax = (int)(origBox[i].xmax);
        box.ymin = (int)(origBox[i].ymin);
        box.ymax = (int)(origBox[i].ymax);

        int boxWidth = box.xmax - box.xmin + 1;
        int boxHeight = box.ymax - box.ymin + 1;
        HI_ASSERT(boxWidth > 0 && boxWidth <= src->u32Width);
        HI_ASSERT(boxHeight > 0 && boxHeight <= src->u32Height);

        /*
            * box的width/height调整为2的倍数
            * Adjust the width/height of the box to a multiple of 2
            */
        if (boxWidth == 1 || boxHeight == 1) {
            printf("box dstWidth=1 && dstHeight=1\n");
            return -1;
        }
        if (boxWidth % HI_OVEN_BASE) {
            box.xmax--;
            boxWidth--;
        }
        if (boxHeight % HI_OVEN_BASE) {
            box.ymax--;
            boxHeight--;
        }

        ret = IveImgCreate(&(pstLBPH[i].stGrayImg), IVE_IMAGE_TYPE_U8C1, boxWidth, boxHeight);
        HI_ASSERT(!ret);

        /*
        * 从box的源地址复制到目的地址
        * Copy box from src to dst
        */
        int srcStride = src->au32Stride[0];
        int dstStride = pstLBPH[i].stGrayImg.au32Stride[0];
        uint8_t *srcBuf = (uint8_t*)((uintptr_t)src->au64VirAddr[0]);
        uint8_t *dstBuf = (uint8_t*)((uintptr_t)pstLBPH[i].stGrayImg.au64VirAddr[0]);
        uint8_t *srcPtr = &srcBuf[box.ymin * srcStride];
        uint8_t *dstPtr = dstBuf;
        for (int h = 0; h < boxHeight; h++, srcPtr += srcStride, dstPtr += dstStride) {
            if (memcpy_s(dstPtr, boxWidth, srcPtr + box.xmin, boxWidth) != EOK) {
                HI_ASSERT(0);
            }
        }
        HI_ASSERT(dstPtr - dstBuf == boxHeight * dstStride);
        
        /*
        * 当user未指定dstWidth/dstHeight，或指定的值与box相同时，无需resize
        *
        * When the user does not specify dstWidth/dstHeight,
        * or the specified value is the same as the box, there is no need to resize
        */
        if (dstWidth <= 0 || dstHeight <= 0 ||
            (dstWidth == boxWidth && dstHeight == boxHeight)) {
            continue;
        }

        /*
        * 缩放
        * Resize
        */
        IVE_IMAGE_S realDst;
        ret = IveImgResize(&(pstLBPH[i].stGrayImg), &realDst, dstWidth, dstHeight);
        IveImgDestroy(&(pstLBPH[i].stGrayImg));
        SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != ret, ret, "Error(%#x), yuv_cut_to_u8c1 FAIL!\n", ret);
        pstLBPH[i].stGrayImg = realDst;
    }
    return ret;
}

static HI_S32 IVE_LBPH_GRAY_TO_EQUALIZEHIST(SAMPLE_IVE_LBPH_S *pstLBPH)
{
    HI_U8 i;
    HI_S32 s32Ret;
    
    IVE_HANDLE hIveHandle;
    HI_BOOL bInstant = HI_TRUE;
    HI_BOOL bFinish = HI_FALSE;
    HI_BOOL bBlock = HI_TRUE;

    for (i = 0; i < matchTargetNums; i++)
    {
        CHECK_NULL_PTR(&pstLBPH[i].stGrayImg);
        CHECK_NULL_PTR(&pstLBPH[i].stEqualHistImg);
        
        s32Ret = HI_MPI_IVE_EqualizeHist(&hIveHandle, &pstLBPH[i].stGrayImg, &pstLBPH[i].stEqualHistImg, 
            &pstLBPH[i].stEaualHistCtrl, bInstant);
        SAMPLE_CHECK_EXPR_RET(s32Ret != HI_SUCCESS, s32Ret, "Error(%#x),num %d : HI_MPI_IVE_EqualizeHist failed!\n", s32Ret, i);

        if (HI_TRUE == bInstant) {
            s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
            while (s32Ret == HI_ERR_IVE_QUERY_TIMEOUT) {
                usleep(100); /* sleep 100 us */
                s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
            }
            SAMPLE_CHECK_EXPR_RET(s32Ret != HI_SUCCESS, s32Ret, "Error(%#x),HI_MPI_IVE_Query failed!\n", s32Ret);
        }
    }
    return s32Ret;
}

static HI_S32 IVE_LBPH_EQUALIZEHIST_TO_LBP(SAMPLE_IVE_LBPH_S *pstLBPH)
{
    HI_U8 i;
    HI_S32 s32Ret;
    
    IVE_HANDLE hIveHandle;
    HI_BOOL bInstant = HI_TRUE;
    HI_BOOL bFinish = HI_FALSE;
    HI_BOOL bBlock = HI_TRUE;

    for (i = 0; i < matchTargetNums; i++)
    {
        CHECK_NULL_PTR(&pstLBPH[i].stEqualHistImg);
        CHECK_NULL_PTR(&pstLBPH[i].stLbpImg);

        s32Ret = HI_MPI_IVE_LBP(&hIveHandle, &pstLBPH[i].stEqualHistImg, &pstLBPH[i].stLbpImg, 
            &pstLBPH[i].stLbpCtrl, bInstant);
        SAMPLE_CHECK_EXPR_RET(s32Ret != HI_SUCCESS, s32Ret, "Error(%#x),num %d : HI_MPI_IVE_LBP failed!\n", s32Ret, i);

        if (HI_TRUE == bInstant) {
            s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
            while (s32Ret == HI_ERR_IVE_QUERY_TIMEOUT) {
                usleep(100); /* sleep 100 us */
                s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
            }
            SAMPLE_CHECK_EXPR_RET(s32Ret != HI_SUCCESS, s32Ret, "Error(%#x),HI_MPI_IVE_Query failed!\n", s32Ret);
        }
    }
    return s32Ret;
}

static HI_VOID IVE_LBPH_LOCAL_TEXTURE_CAL(const IVE_IMAGE_S *src, HI_U32 *feature_size, HI_BOOL isTarget)
{
    uint8_t src_width = src->u32Width;
    uint8_t *srcBuf = (uint8_t*)((uintptr_t)src->au64VirAddr[0]);

    uint8_t *dstPtr = NULL;
    if (isTarget == HI_TRUE)
    {
        dstPtr = (uint8_t*)((uintptr_t)s_stMatchParams.stMatchMem.u64VirAddr);
    } else {
        dstPtr = (uint8_t*)((uintptr_t)s_stMatchParams.stFeatureMem.u64VirAddr);
    }

    uint8_t block_hist[UNIFORM_PATTERN_COUNT] = { 0 };
    uint8_t block_rows = src->u32Width / s_stMatchParams.grid_w;
    uint8_t block_cols = src->u32Height / s_stMatchParams.grid_h;

    for (int i = 0; i < block_cols; i++)
    {
        for (int j = 0; j < block_rows; j++)
        {
            /* get every block hist, total 8 * 8 = 64 blocks */
            for (int k = 0; k < s_stMatchParams.grid_h; k++)
            {
                for (int l = 0; l < s_stMatchParams.grid_w; l++)
                {
                    /* 
                     * 旋转不变模式，减少图像旋转导致的特征突变
                     * Rotation invariant mode reduces feature mutation caused by image rotation
                     */
                    uint8_t lbp = srcBuf[l + src_width * ( k + i * s_stMatchParams.grid_h ) + (j * s_stMatchParams.grid_w)];
                    // uint8_t min_val = lbp;
                    // for (int m = 0; m < 8; m++)
                    // {
                    //     lbp = (lbp >> 1) | ((lbp & 1) << 7); // 循环右移一位
                    //     if (lbp < min_val) {
                    //         min_val = lbp;
                    //     }
                    // }
                    /* 
                     * 均匀模式LBP映射表，能够对特征量进行降维，提高抗噪能力
                     * Uniform mode LBP mapping table can reduce the dimension of feature quantity and improve noise resistance
                     */
                    uint8_t uniform_val = uniform_patterns[lbp];
                    block_hist[uniform_val] += 1;
                }
            }
            (HI_VOID)memcpy_s(dstPtr + sizeof(block_hist) * (i * block_rows + j), sizeof(block_hist), block_hist, sizeof(block_hist));
            (HI_VOID)memset_s(block_hist, sizeof(block_hist), 0, sizeof(block_hist));
        }
    }
    *feature_size = block_rows * block_cols * UNIFORM_PATTERN_COUNT;
}

static HI_FLOAT IVE_LBPH_LOCAL_CMP_SCORE(HI_U32 size)
{
    HI_U8 *match_vectors = (HI_U8 *)(s_stMatchParams.stMatchMem.u64VirAddr);
    HI_U8 *feature_vectors = (HI_U8 *)(s_stMatchParams.stFeatureMem.u64VirAddr);

    HI_FLOAT score = 0;

    /* 
     * 计算输入人脸图像与跟踪人脸图像的特征向量相似度  卡方距离
     * Calculate the similarity of the feature vectors of the input face image and the tracked face image
     */
    for (int i = 0; i < size; i++)
    {
        if (match_vectors[i] != 0 && feature_vectors[i] != 0)
        {
            HI_S16 diff = match_vectors[i] - feature_vectors[i];
            score += (diff * diff) / (match_vectors[i] + feature_vectors[i]);
        }
    }

    return score;
}

static HI_VOID IVE_LBPH_DEALWITH_FRAME(SAMPLE_IVE_FEATURE_MATCH_S *pstMatchParams)
{
    HI_U8 i, match_idx = 0;
    HI_U32 feature_nums;
    HI_FLOAT match_score, min_score = INT_MAX;

    /* performances variables */
    struct timeval tm;
    _meas_time_start(&tm);

    /* 3 steps finish face img tran to lbp img. */
    /* step 1: get face gray img from frame */
    IVE_LBPH_FRAME_TO_GRAY(&(srcImg), s_stLBPH, faceRectBboxs, LBPH_IMG_WIDTH, LBPH_IMG_HEIGHT);

    /* step 2: caculate equalizeHist from gray img */
    IVE_LBPH_GRAY_TO_EQUALIZEHIST(s_stLBPH);

    /* step 3: caculate LBP from equalizeHist img */
    IVE_LBPH_EQUALIZEHIST_TO_LBP(s_stLBPH);

    /* 
     * 完成以上的LBP算子计算图像后，对LBP图像进行特征匹配，求出匹配成功的人脸图像
     * After completing the above LBP operator calculation image, 
     * perform feature matching on the LBP image to find the successfully matched face image
     */
    for (i = 0; i < matchTargetNums; i++)
    {
        /* 
         * 对LBP图像进行局部纹理特征直方图统计
         * Perform local texture feature histogram statistics on LBP images
         */
        IVE_LBPH_LOCAL_TEXTURE_CAL(&(s_stLBPH[i].stLbpImg), &feature_nums, pstMatchParams->isNewMatch);
        if (pstMatchParams->isNewMatch == HI_TRUE)
        {
            pstMatchParams->isUpdate = HI_TRUE;
            return;
        }

        /* 
         * 对全部LBP图像进行匹配分数计算
         * Calculate the matching scores for all LBP images
         */
        match_score = IVE_LBPH_LOCAL_CMP_SCORE(feature_nums);
        printf("match _score : %.2f\n", match_score);
        if (match_score < min_score)
        {
            min_score = match_score;
            match_idx = i;
        }

        IveImgDestroy(&(s_stLBPH[i].stGrayImg));
    }
    
    /* 
     * 对匹配结果赋值
     * Assigning values ​​to matching results
     */
    if (pstMatchParams->isNewMatch == HI_FALSE)
    {
        pstMatchParams->bBox = faceRectBboxs[match_idx];
        pstMatchParams->score = min_score;
        printf("min score: %.2f\n", pstMatchParams->score);
        pstMatchParams->isUpdate = HI_TRUE;
    }

    _meas_time_stop(tm, __FUNCTION__);
}

/* 
 * 添加视频帧及目标识别数据至队列
 * Add video frames and target recognition data to the queue
 */
HI_VOID IVE_LBPH_READY_FOR_TASK(VIDEO_FRAME_INFO_S *frm, SVP_NNIE_YOLOV5_BBOX_S *res, HI_BOOL isNewMatch)
{
    static hi_u8 task_tick = 0;

    /* step 1: get the dection target nums. */
    faceTargetNums = 0;
    while(res != NULL)
    {
        if (res->u8ClassIdx == 0)
        {
            if (faceTargetNums < TRACK_MAX_NUM)
            {
                faceRectBboxs[faceTargetNums++] = res->bBox;
            } else {
                faceTargetNums++;
            }
        }
        res = res->next;
    }

    if (task_tick < 5)
    {
        task_tick++;
        lastTargetNums = faceTargetNums;
        return;
    } else {
        task_tick = 0;
    }

    if ((faceTargetNums == 0) || (lastTargetNums != faceTargetNums) || (s_stMatchParams.isProcess == HI_TRUE))
    {
        lastTargetNums = faceTargetNums;
        return; 
    }

    hi_u32 s32Ret;
    HI_BOOL bInstant = HI_TRUE;

    /* step 2: add video frame */
    s32Ret = SAMPLE_COMM_IVE_DmaImage(frm, &srcImg, bInstant);
    if (s32Ret != HI_SUCCESS) {
        printf("[ERROR] : [GetFaceDectionBboxs] SAMPLE_COMM_IVE_DmaImage fail\n");
        lastTargetNums = faceTargetNums;
        return;
    }

    matchTargetNums = faceTargetNums >= TRACK_MAX_NUM? TRACK_MAX_NUM : faceTargetNums;
    for (int i = 0; i < matchTargetNums; i++)
    {
        RectBoxTran(&faceRectBboxs[i], YOLO_MODEL_WIDTH, YOLO_MODEL_HEIGHT, frm->stVFrame.u32Width, frm->stVFrame.u32Height);
    }

    s_stMatchParams.isTaskReady = HI_TRUE;
    s_stMatchParams.isNewMatch = isNewMatch;
    lastTargetNums = faceTargetNums;
}

/* 
 * 获取LBPH特征匹配算法图像结果
 * Get the image results of the LBPH feature matching algorithm
 */
HI_BOOL IVE_LBPH_RETURN_RESULT(RectBox *bbox, HI_FLOAT *score, HI_BOOL *isNewTemp)
{
    if (s_stMatchParams.isUpdate == HI_TRUE)
    {
        if (s_stMatchParams.isNewMatch == HI_TRUE)
        {
            *isNewTemp = HI_TRUE;
        } else {
            *bbox = s_stMatchParams.bBox;
            *score = s_stMatchParams.score;
            *isNewTemp = HI_FALSE;
        }
        s_stMatchParams.isUpdate = HI_FALSE;
        return HI_TRUE;
    }
    return HI_FALSE;
}

/*
 * 初始化均匀LBP映射表
 * Initialize uniform LBP mapping table
 */
static HI_VOID Init_Uniform_Patterns( void )
{
    int index = 0;
    for (int i = 0; i < IVE_HIST_NUM; i++)
    {
        int count = 0;
        for (int j = 0; j < 8; j++)
        {
            count += (((i >> j) & 1) != ((i >> ((j + 1) % 8)) & 1));
        }
        if (count <= 2)
        {
            uniform_patterns[i] = index++;
        } else {
            uniform_patterns[i] = UNIFORM_PATTERN_COUNT - 1;
        }
    }
}

/* function : uart process */
HI_VOID* IveLbphProcess(HI_VOID* pArgs)
{
    hi_unused(pArgs);

    HI_S32 s32Ret;

    /* step 1: init LBP && Hist operator struct */
    s_bStopSignal = HI_FALSE;
    s32Ret = SAMPLE_IVE_LBPH_Init(s_stLBPH, &s_stMatchParams,LBPH_IMG_WIDTH, LBPH_IMG_HEIGHT);
    if (s32Ret == HI_SUCCESS) printf("[INFO] : [IveLbphProcess] SAMPLE_IVE_LBPH_Init success.\n");

    /* step 2: init LBP uniform patterns */
    Init_Uniform_Patterns();

    while(s_bStopSignal == HI_FALSE)
    {
        /* 
         * 更新有效视频帧，对视频帧进行LBPH特征匹配
         * Update the valid video frame and perform LBPH feature matching on the video frame
         */
        if (s_stMatchParams.isTaskReady == HI_TRUE)
        {
            s_stMatchParams.isTaskReady = HI_FALSE;
            s_stMatchParams.isProcess = HI_TRUE;
            IVE_LBPH_DEALWITH_FRAME(&s_stMatchParams);
            s_stMatchParams.isProcess = HI_FALSE;
        } else {
            usleep(100); /* sleep 100 us */
        }
    }
    return HI_NULL;
}

HI_VOID SAMPLE_IVE_LBPH_Stop( void )
{
    SAMPLE_IVE_LBPH_Uninit(s_stLBPH, &s_stMatchParams);
    printf("[INFO] : [IveLbphProcess] SAMPLE_IVE_LBPH_Uninit success.\n");
}
